﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Convertor.Base;
using zijian666.Converts.Core;
using zijian666.Converts.Extensions;

namespace zijian666.Converts.Convertor;

/// <summary>
/// 匿名类型转换器
/// </summary>
/// <typeparam name="T"></typeparam>
public class AnonymousTypeConvertor<T> : AllowNullConvertor<T>
                                       , IFrom<object, T>
                                       , IFrom<string, T>
{
    /// <summary>
    /// 匿名类型构造函数
    /// </summary>
    private readonly Func<object?[], T> _constructor;

    /// <summary>
    /// 匿名类型构造函数参数列表
    /// </summary>
    private readonly ParameterInfo[] _parameters;

    /// <summary>
    /// 构造函数
    /// </summary>
    public AnonymousTypeConvertor()
    {
        var constructor = OutputType.GetConstructors()[0];
        var parameters = constructor.GetParameters();

        var p = Expression.Parameter(typeof(object?[]), "p");
        var args = new UnaryExpression[parameters.Length];

        for (var i = 0; i < args.Length; i++)
        {
            var index = Expression.ArrayAccess(p, Expression.Constant(i));
            args[i] = Expression.Convert(index, parameters[i].ParameterType);
        }

        var @new = Expression.New(constructor, args);
        // 将匿名对象的构造函数构造为委托
        _constructor = Expression.Lambda<Func<object?[], T>>(@new, p).Compile();
        _parameters = parameters;
    }
    /// <inheritdoc/>
    public ConvertResult<T> From(IConvertContext context, string input)
    {
        var dict = context.Settings.StringSerializer?.ToObject(input, typeof(Dictionary<string, object>));
        if (dict is not null)
        {
            return From(context, dict);
        }
        return context.ConvertFail(this, input);
    }

    /// <inheritdoc/>
    public ConvertResult<T> From(IConvertContext context, object input)
    {
        var enumerator = new KeyValueEnumerator<string, object>(context, input);
        if (!enumerator.KeyIsString)
        {
            return context.ConvertFail(this, input);
        }
        var arguments = new object?[_parameters.Length];

        var matched = 0;
        while (enumerator.MoveNext())
        {
            var key = enumerator.GetKey();
            if (!key.Success)
            {
                var rs = context.Settings.GetResourceStrings();
                var message = string.Format(rs.COLLECTION_KEY_FAIL, TypeFriendlyName, enumerator.OriginalKey);
                return new InvalidCastException(message, key.Exception);
            }

            var parameter = _parameters.FirstOrDefault(x => x.Name.Equals(key.Value, StringComparison.OrdinalIgnoreCase));
            if (parameter is null)
            {
                continue;
            }

            matched++;
            var value = context.Convert(parameter.ParameterType, enumerator.OriginalValue);
            if (!value.Success)
            {
                var rs = context.Settings.GetResourceStrings();
                var message = string.Format(rs.COLLECTION_ADD_FAIL, TypeFriendlyName, key.Value, enumerator.OriginalValue);
                return new InvalidCastException(message, key.Exception);
            }

            arguments[parameter.Position] = value.Value;
        }
        // 如果完全没有匹配参数,则返回转换失败
        if (matched == 0)
        {
            return context.ConvertFail(this, input);
        }
        // 匹配参数与实际参数不一致,则将缺失的参数填充默认值
        if (matched != arguments.Length)
        {
            for (var i = 0; i < arguments.Length; i++)
            {
                if (arguments[i] is null)
                {
                    arguments[i] = _parameters[i].ParameterType.GetDefault();
                }
            }
        }

        return _constructor(arguments);
    }
}
